package org.rslai.tcedit.security.SecureFilter;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.SecurityConfig;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.security.util.AntUrlPathMatcher;
import org.springframework.security.util.RegexUrlPathMatcher;
import org.springframework.security.util.UrlMatcher;

/**
 * @author rslai
 */
public class UrlInvocationDefinitionSource implements FilterInvocationDefinitionSource, InitializingBean {
	protected Log log = LogFactory.getLog(getClass());

	private UrlMatcher urlMatcher;
	private boolean useAntPath = true;
	private boolean lowercaseComparisons = true;

	/**
	 * @param useAntPath the useAntPath to set
	 */
	public void setUseAntPath(boolean useAntPath) {
		this.useAntPath = useAntPath;
	}

	/**
	 * @param lowercaseComparisons
	 */
	public void setLowercaseComparisons(boolean lowercaseComparisons) {
		this.lowercaseComparisons = lowercaseComparisons;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
	 */
	public void afterPropertiesSet() throws Exception {

		// default url matcher will be RegexUrlPathMatcher
		this.urlMatcher = new RegexUrlPathMatcher();

		if (useAntPath) { // change the implementation if required
			this.urlMatcher = new AntUrlPathMatcher();
		}

		// Only change from the defaults if the attribute has been set
		if ("true".equals(lowercaseComparisons)) {
			if (!this.useAntPath) {
				((RegexUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(true);
			}
		} else if ("false".equals(lowercaseComparisons)) {
			if (this.useAntPath) {
				((AntUrlPathMatcher) this.urlMatcher).setRequiresLowerCaseUrl(false);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * org.springframework.security.intercept.ObjectDefinitionSource#getAttributes(java.lang.Object)
	 */
	public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException {
		FilterInvocation filterInvocation = (FilterInvocation) filter;
		String requestURI = org.rslai.tcedit.util.StrUtil.split(filterInvocation.getRequestUrl(), "?")[0];

		Map<String, List<String>> urlAuthorities = this.getUrlAuthorities(filterInvocation);
		for (Iterator<Map.Entry<String, List<String>>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {
			Map.Entry<String, List<String>> entry = iter.next();

			if (urlMatcher.pathMatchesUrl(entry.getKey(), requestURI)) {
				List<ConfigAttribute> attributes = new LinkedList<ConfigAttribute>();
				for (String roleName : entry.getValue()) {
					attributes.add(new SecurityConfig(roleName));
				}

				if (log.isDebugEnabled()) {
					StringBuffer logStr = new StringBuffer();
					logStr.append("请求URL允许访问角色：");
					logStr.append(requestURI);
					logStr.append("=");
					logStr.append(attributes);
					log.debug(logStr.toString());
				}

				return new ConfigAttributeDefinition(attributes);
			}
		}

		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
	 */
	@SuppressWarnings("unchecked")
	public Collection getConfigAttributeDefinitions() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	public boolean supports(Class clazz) {
		return true;
	}

	/**
	 * 从 ServletContext 取回 urlAuthorities
	 * @param filterInvocation
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Map<String, List<String>> getUrlAuthorities(FilterInvocation filterInvocation) {
		ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
		return (Map<String, List<String>>) servletContext.getAttribute("urlAuthorities");
	}

}